#include "config.h"

#include "ms2sim.h"

#include <disksim/disksim_interface.h>

#define SECTOR	512

static double	next_event = -1;
static struct disksim_interface	*disksim;
static ecm_uint32	n_sectors;

static ecm_uint32	*media_blkno_start;

extern media_t *get_media(ecm_uint32 mid);

/* diskmodel */
struct dm_disk_if {
	// some general parameters
	int dm_cyls;

	//  int dm_tracks;  // what needs this?
	int dm_surfaces;
	int dm_sectors;

	/* layout and mech omitted. */
};

static void
schedule_callback(disksim_interface_callback_t fn, double t, void *ctx)
{
	next_event = t;
}

static void
deschedule_callback(double t, void *ctx)
{
	next_event = -1;
}

static void
report_completion(double t, struct disksim_request *r, void *ctx)
{
}

ecm_uint64
invoke_disksim(ecm_uint64 ts, ecm_uint32 mid, ecm_uint32 bid)
{
	struct disksim_request	r;
	ecm_double	start, now;

	start = now = ts / 1000.0;

	r.start = now;
	r.flags = DISKSIM_READ;
	r.devno = 0;

	r.blkno = media_blkno_start[mid - 1] + bid * n_sectors;
	r.bytecount = disk_blksize;

	disksim_interface_request_arrive(disksim, now, &r);

	while (next_event >= 0) {
		now = next_event;
		next_event = -1;
		disksim_interface_internal_event(disksim, now, 0);
	}
	
	return (ecm_uint64)(((now - start) * 1000) / disksim_raid);
}

static ECMBOOL
setup_start_blkno(void)
{
	media_t	*media;
	ecm_uint32	i;
	struct dm_disk_if *dm_disk;

	dm_disk = disksim_getdiskmodel(disksim, 0);
	media_blkno_start = (ecm_uint32 *)ecm_malloc(sizeof(ecm_uint32) * n_medias);

	media_blkno_start[0] = 0;
	for (i = 1; i < n_medias; i++) {
		media = get_media(i + 1);
		media_blkno_start[i] = media_blkno_start[i - 1] + media->bid_max * n_sectors;
	}

	if (dm_disk->dm_sectors < media_blkno_start[n_medias - 1] + media->bid_max * n_sectors) {
		ERR("disk too small");
		return ECM_FALSE;
	}

	return ECM_TRUE;
}

ECMBOOL
init_disksim(void)
{
	if (ecm_empty_str(disksim_param))
		return ECM_TRUE;

	ECMASSERT(disk_blksize % SECTOR == 0);

	n_sectors = disk_blksize / SECTOR;

	disksim = disksim_interface_initialize(disksim_param, "disksim.out",
					       report_completion,
					       schedule_callback,
					       deschedule_callback, 0, 0, 0);

	if (!setup_start_blkno())
		return ECM_FALSE;

	disksim_enabled = ECM_TRUE;

	return ECM_TRUE;
}
